/*
 *	Chemical formatter
 *
 *	Copyright (C) 2011 XiaoJSoft Studio. All Rights Reserved.
 *	Copyright (C) Ji WenCong <whs_jwc@163.com>
 *
 *	This program is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *
 *	You should have received a copy of the GNU General Public License
 *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

#define _GNU_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include "../include/format.h"
#include "../include/strext.h"
#include "../include/mmdiv.h"

/*
 *	int* lfToInt(long double *in, int inLen);
 *
 *	Converts a long double heap into a integer heap.
 *
 *	*in		->	the pointer pointed to the long double heap,
 *	inLen	->	the length of the long double heap.
 */
int* lfToInt(long double *in, int inLen)
{
	int *ret;
	int idx;

	/*  Allocating enough memory areas to contain the integer heap  */
	ret = (int*) malloc(inLen * sizeof(int));
	if (ret == NULL) {
		return(NULL);
	}

	/*  Converting the long double heap into the integer heap which we allocated before  */
	for (idx = 1; idx <= inLen; idx++) {
		*(ret + idx - 1) = (int) *(in + idx - 1);
	}

	return(ret);
}

/*
 *	char* formatResult(char *szEquation, int *bce, int bceLength);
 *
 *	Formats the chemical equations with balanced values.
 *
 *	*szEquation	->	the chemical equation,
 *	*bce			->	the balanced values,
 *	bceLength		->	the count of balanced values.
 */
char* formatResult(char *szEquation, int *bce, int bceLength)
{
	collection *spSides;
	collection *spSideA, *spSideB;
	collection *spFromA, *spFromB;
	collection *cur;
	char *szCur;
	char *szTemp;
	char *fxCur;
	int idx;
	int pfx;
	char *ret;
	int lenA, lenB;

	/*  Cutting the chemical equation into two halves  */
	spSides = sliceString_expr(szEquation, "=");
	if (spSides == NULL) {
		return(NULL);
	}

	if (getCollectionCount(spSides) != 2) {
		freeCollection(spSides);
		return(NULL);
	}

	spSideA = getCollectionItem(spSides, 1);
	spSideB = getCollectionItem(spSides, 2);
	if (spSideA == NULL || spSideB == NULL) {
		freeCollection(spSides);
		return(NULL);
	}

	/*  To get reactants & products  */
	spFromA = sliceString_expr(spSideA->string, "+");
	if (spFromA == NULL) {
		freeCollection(spSides);
		return(NULL);
	}
	spFromB = sliceString_expr(spSideB->string, "+");
	if (spFromB == NULL) {
		freeCollection(spFromA);
		freeCollection(spSides);
		return(NULL);
	}
	spSideA = NULL;
	spSideB = NULL;
	freeCollection(spSides);

	/*  Checking the number of reactants & products  */
	if ((getCollectionCount(spFromA) + getCollectionCount(spFromB)) != bceLength) {
		freeCollection(spFromA);
		freeCollection(spFromB);
		return(NULL);
	}

	/*  We need to use getCollectionCount(spFrom*) many times, but we don't need to do it so many times  */
	lenA = getCollectionCount(spFromA);
	lenB = getCollectionCount(spFromB);

	/*  Generating a empty string  */
	ret = (char*) malloc(1 * sizeof(char));
	if (ret == NULL) {
		freeCollection(spFromA);
		freeCollection(spFromB);
		return(NULL);
	}
	*ret = '\0';

	/*  Connecting balanced values & chemical equation  */

	/*  Part one, reactants  */
	for (idx = 1; idx <= lenA; idx++) {
		if (*(bce + idx - 1) <= 0) {
			continue;
		}

		cur = getCollectionItem(spFromA, idx);
		if (cur == NULL) {
			freeCollection(spFromA);
			freeCollection(spFromB);
			return(NULL);
		}

		szCur = cur->string;
		pfx = (int) strtod_ext(szCur, &fxCur);
		pfx = (pfx <= 1) ? 1 : pfx;
		szCur = NULL;

		/*  Here are all situations we met  */
		if (idx < lenA) {
			if (*(bce + idx - 1) == 1) {
				if (pfx == 1) {
					kasprintf(&szTemp, "%s%s+", ret, fxCur);
				} else {
					kasprintf(&szTemp, "%s%d%s+", ret, pfx, fxCur);
				}
			} else {
				if (strin_ext_expr(1, fxCur, ".") != 0) {
					if (pfx == 1) {
						kasprintf(&szTemp, "%s%d(%s)+", ret, *(bce + idx - 1), fxCur);
					} else {
						kasprintf(&szTemp, "%s%d(%d%s)+", ret, *(bce + idx - 1), pfx, fxCur);
					}
				} else {
					kasprintf(&szTemp, "%s%d%s+", ret, pfx * (*(bce + idx - 1)), fxCur);
				}
			}
		} else {
			if (*(bce + idx - 1) == 1) {
				if (pfx == 1) {
					kasprintf(&szTemp, "%s%s=", ret, fxCur);
				} else {
					kasprintf(&szTemp, "%s%d%s=", ret, pfx, fxCur);
				}
			} else {
				if (strin_ext_expr(1, fxCur, ".") != 0) {
					if (pfx == 1) {
						kasprintf(&szTemp, "%s%d(%s)=", ret, *(bce + idx - 1), fxCur);
					} else {
						kasprintf(&szTemp, "%s%d(%d%s)=", ret, *(bce + idx - 1), pfx, fxCur);
					}
				} else {
					kasprintf(&szTemp, "%s%d%s=", ret, pfx * (*(bce + idx - 1)), fxCur);
				}
			}
		}
		if (szTemp == NULL) {
			freeCollection(spFromA);
			freeCollection(spFromB);
			return(NULL);
		}
		free(ret);
		ret = szTemp;
	}

	/*  Part two, products  */
	for (idx = 1; idx <= lenB; idx++) {
		if (*(bce + lenA + idx - 1) <= 0) {
			continue;
		}

		cur = getCollectionItem(spFromB, idx);
		if (cur == NULL) {
			freeCollection(spFromA);
			freeCollection(spFromB);
			return(NULL);
		}

		szCur = cur->string;
		pfx = (int) strtod_ext(szCur, &fxCur);
		pfx = (pfx <= 1) ? 1 : pfx;
		szCur = NULL;

		/*  Here are all situations we met  */
		if (*(bce + idx + lenA - 1) == 1) {
			if (idx < lenB) {
				if (strin_ext_expr(1, fxCur, ".") != 0) {
					if (pfx == 1) {
						kasprintf(&szTemp, "%s%s+", ret, fxCur);
					} else {
						kasprintf(&szTemp, "%s%d%s+", ret, pfx, fxCur);
					}
				} else {
					if (pfx == 1) {
						kasprintf(&szTemp, "%s%s+", ret, fxCur);
					} else {
						kasprintf(&szTemp, "%s%d%s+", ret, pfx, fxCur);
					}
				}
			} else {
				if (strin_ext_expr(1, fxCur, ".") != 0) {
					if (pfx == 1) {
						kasprintf(&szTemp, "%s%s", ret, fxCur);
					} else {
						kasprintf(&szTemp, "%s%d%s", ret, pfx, fxCur);
					}
				} else {
					if (pfx == 1) {
						kasprintf(&szTemp, "%s%s", ret, fxCur);
					}
					else
					{
						kasprintf(&szTemp, "%s%d%s", ret, pfx, fxCur);
					}
				}
			}
		} else {
			if (idx < lenB) {
				if (strin_ext_expr(1, fxCur, ".") != 0) {
					if (pfx == 1) {
						kasprintf(&szTemp, "%s%d(%s)+", ret, *(bce + lenA + idx - 1), fxCur);
					} else {
						kasprintf(&szTemp, "%s%d(%d%s)+", ret, *(bce + lenA + idx - 1), pfx, fxCur);
					}
				} else {
					kasprintf(&szTemp, "%s%d%s+", ret, pfx * (*(bce + lenA + idx - 1)), fxCur);
				}
			} else {
				if (strin_ext_expr(1, fxCur, ".") != 0) {
					if (pfx == 1) {
						kasprintf(&szTemp, "%s%d(%s)", ret, *(bce + lenA + idx - 1), fxCur);
					} else {
						kasprintf(&szTemp, "%s%d(%d%s)", ret, *(bce + lenA + idx - 1), pfx, fxCur);
					}
				} else {
					kasprintf(&szTemp, "%s%d%s", ret, pfx * (*(bce + lenA + idx - 1)), fxCur);
				}
			}
		}
		if (szTemp == NULL) {
			freeCollection(spFromA);
			freeCollection(spFromB);
			return(NULL);
		}
		free(ret);
		ret = szTemp;
	}

	freeCollection(spFromA);
	freeCollection(spFromB);

	return(ret);
}

/*
 *	char* formatResult2(char *szEquation);
 *
 *	Formats the chemical equations.
 *
 *	*szEquation	->	the chemical equation.
 */
char* formatResult2(char *szEquation)
{
	int lpMDiv = 1;
	int psInc1 = 0, psInc2 = 0;
	int scanIdx;
	int freeSwitch = 0;
	char *exc;
	collection *spSides;
	collection *spSideA, *spSideB;
	collection *spFromA, *spFromB;
	collection *cur;
	char *szCur;
	char *szTemp;
	char *fxCur;
	int idx;
	int pfx;
	char *ret;
	int lenA, lenB;

	/*  Cutting the chemical equation into two halves  */
	spSides = sliceString_expr(szEquation, "=");
	if (spSides == NULL) {
		return(NULL);
	}

	if (getCollectionCount(spSides) != 2) {
		freeCollection(spSides);
		return(NULL);
	}

	spSideA = getCollectionItem(spSides, 1);
	spSideB = getCollectionItem(spSides, 2);
	if (spSideA == NULL || spSideB == NULL) {
		freeCollection(spSides);
		return(NULL);
	}

	/*  To get reactants & products  */
	spFromA = sliceString_expr(spSideA->string, "+");
	if (spFromA == NULL) {
		freeCollection(spSides);
		return(NULL);
	}
	spFromB = sliceString_expr(spSideB->string, "+");
	if (spFromB == NULL) {
		freeCollection(spFromA);
		freeCollection(spSides);
		return(NULL);
	}
	spSideA = NULL;
	spSideB = NULL;
	freeCollection(spSides);

	/*  We need to use getCollectionCount(spFrom*) many times, but we don't need to do it so many times  */
	lenA = getCollectionCount(spFromA);
	lenB = getCollectionCount(spFromB);

	/*  Generating a empty string  */
	ret = (char*) malloc(1 * sizeof(char));
	if (ret == NULL) {
		freeCollection(spFromA);
		freeCollection(spFromB);
		return(NULL);
	}
	*ret = '\0';

	/*  To get the common division of balanced values  */
	for (idx = 1; idx <= lenA; idx++) {
		cur = getCollectionItem(spFromA, idx);
		if (cur == NULL) {
			freeCollection(spFromA);
			freeCollection(spFromB);
			return(NULL);
		}
		szCur = cur->string;
		pfx = (int) strtod_ext(szCur, &fxCur);
		pfx = (pfx <= 1) ? 1 : pfx;
		if (idx == 1) {
			if (strin_ext_expr(1, fxCur, ".") != 0) {
				lpMDiv = 1;
			} else {
				lpMDiv = pfx;
			}
		} else {
			if (strin_ext_expr(1, fxCur, ".") != 0) {
				lpMDiv = 1;
			} else {
				lpMDiv = (int) mdiv(lpMDiv, pfx);
			}
		}
		szCur = NULL;
	}
	for (idx = 1; idx <= lenB; idx++) {
		cur = getCollectionItem(spFromB, idx);
		if (cur == NULL) {
			freeCollection(spFromA);
			freeCollection(spFromB);
			return(NULL);
		}
		szCur = cur->string;
		pfx = (int) strtod_ext(szCur, &fxCur);
		pfx = (pfx <= 1) ? 1 : pfx;
		if (strin_ext_expr(1, fxCur, ".") != 0) {
			lpMDiv = 1;
		} else {
			lpMDiv = (int) mdiv(lpMDiv, pfx);
		}
		szCur = NULL;
	}

	/*  Formatting new strings  */

	/*  Part one, reactants  */
	for (idx = 1; idx <= lenA; idx++) {
		cur = getCollectionItem(spFromA, idx);
		if (cur == NULL) {
			freeCollection(spFromA);
			freeCollection(spFromB);
			return(NULL);
		}
		szCur = cur->string;
		pfx = (int) strtod_ext(szCur, &fxCur);
		pfx = (pfx <= 1) ? 1 : pfx;
		if (pfx / lpMDiv == 1) {

			/*  Solving brackets  */
			while(!0) {
				freeSwitch = 0;
				if (*fxCur == '(') {
					psInc1 = 0; psInc2 = 0;
					for (scanIdx = 0; scanIdx <= (strlen_ext(fxCur) - 1); scanIdx++) {
						if ((*(fxCur + scanIdx)) == '(') {
							psInc1++;
						}
						if ((*(fxCur + scanIdx)) == ')') {
							psInc2++;
						}
						if ((psInc1 == psInc2) && (psInc1 != 0)) {
							break;
						}
					}
					if (scanIdx == (strlen_ext(fxCur) - 1)) {
						exc = fxCur;
						fxCur = strmid_ext(fxCur, 2, strlen_ext(fxCur) - 2);
						if (freeSwitch != 0) {
							free(exc);
						} else {
							freeSwitch = 1;
						}
					} else {
						break;
					}
				} else {
					break;
				}
			}
		}
		szCur = NULL;

		/*  Connecting new strings  */
		if (idx < lenA) {
			if (pfx / lpMDiv == 1) {
				kasprintf(&szTemp, "%s%s+", ret, fxCur);
			} else {
				kasprintf(&szTemp, "%s%d%s+", ret, pfx / lpMDiv, fxCur);
			}
		} else {
			if (pfx / lpMDiv == 1) {
				kasprintf(&szTemp, "%s%s=", ret, fxCur);
			} else {
				kasprintf(&szTemp, "%s%d%s=", ret, pfx / lpMDiv, fxCur);
			}
		}
		if (szTemp == NULL) {
			if (freeSwitch == 1) {
				free(fxCur);
			}
			freeCollection(spFromA);
			freeCollection(spFromB);
			return(NULL);
		}
		if (freeSwitch == 1) {
			free(fxCur);
		}
		free(ret);
		ret = szTemp;
	}

	/*  Part two, products  */
	for (idx = 1; idx <= lenB; idx++) {
		cur = getCollectionItem(spFromB, idx);
		if (cur == NULL) {
			freeCollection(spFromA);
			freeCollection(spFromB);
			return(NULL);
		}
		szCur = cur->string;
		pfx = (int) strtod_ext(szCur, &fxCur);
		pfx = (pfx <= 1) ? 1 : pfx;
		if (pfx / lpMDiv == 1) {
			/*  Solving brackets  */
			while(!0) {
				freeSwitch = 0;
				if (*fxCur == '(') {
					psInc1 = 0; psInc2 = 0;
					for (scanIdx = 0; scanIdx <= (strlen_ext(fxCur) - 1); scanIdx++) {
						if ((*(fxCur + scanIdx)) == '(') {
							psInc1++;
						}
						if ((*(fxCur + scanIdx)) == ')') {
							psInc2++;
						}
						if ((psInc1 == psInc2) && (psInc1 != 0)) {
							break;
						}
					}
					if (scanIdx == (strlen_ext(fxCur) - 1)) {
						exc = fxCur;
						fxCur = strmid_ext(fxCur, 2, strlen_ext(fxCur) - 2);
						if (freeSwitch != 0) {
							free(exc);
						} else {
							freeSwitch = 1;
						}
					} else {
						break;
					}
				} else {
					break;
				}
			}
		}
		szCur = NULL;

		/*  Connecting new strings  */
		if (pfx / lpMDiv == 1) {
			if (idx < lenB) {
				kasprintf(&szTemp, "%s%s+", ret, fxCur);
			} else {
				kasprintf(&szTemp, "%s%s", ret, fxCur);
			}
		} else {
			if (idx < lenB) {
				kasprintf(&szTemp, "%s%d%s+", ret, pfx / lpMDiv, fxCur);
			} else {
				kasprintf(&szTemp, "%s%d%s", ret, pfx / lpMDiv, fxCur);
			}
		}
		if (szTemp == NULL) {
			if (freeSwitch == 1) {
				free(fxCur);
			}
			freeCollection(spFromA);
			freeCollection(spFromB);
			return(NULL);
		}
		if (freeSwitch == 1) {
			free(fxCur);
		}
		free(ret);
		ret = szTemp;
	}
	freeCollection(spFromA);
	freeCollection(spFromB);
	return(ret);
}

